<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="coinfaces">

        <title>Javascript原始类型封装对象</title>

    <link rel="stylesheet" href="../../theme/css/style.css">
    <link rel="stylesheet" href="../../theme/css/pygments.css">



</head>

<body>
    <div class="content">
<h1>Javascript原始类型封装对象</h1>    <p>
        under
            <a href="../../tags/javascript/">javascript</a>
    </p>
    <p>
        in <a href="../../categories/tech/">tech</a>
    </p>
    <p>Published: 2016-08-09</p>


    <p>学习JavaScript的时候疑惑为什么要有原始类型和对象的区分，因为数字、字符串和布尔类型的变量也可以访问对应的<tt class="docutils literal">Number</tt>、<tt class="docutils literal">String</tt>和<tt class="docutils literal">Boolean</tt>变量<tt class="docutils literal">prototype</tt>的属性：</p>
<div class="highlight"><pre><span></span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="nx">toString</span><span class="p">();</span>  <span class="c1">// &quot;2&quot;</span>
</pre></div>
<p>为什么会这样？难道原始类型也可以有<tt class="docutils literal">[[Prototype]]</tt>吗？不是这样的：</p>
<div class="highlight"><pre><span></span><span class="nb">Number</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">typeOf</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">typeof</span> <span class="k">this</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="nx">typeOf</span><span class="p">();</span>  <span class="c1">// &quot;object&quot;</span>
<span class="k">typeof</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// &quot;number&quot;</span>
</pre></div>
<p>看上去在原始类型变量访问“属性”的时候，有对象临时参与进来，但执行完毕后该对象就消失了，造成原始变量也可以访问“属性”的错觉。以上代码相当于：</p>
<div class="highlight"><pre><span></span><span class="nb">Number</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">typeOf</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">typeof</span> <span class="k">this</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">(</span><span class="k">new</span> <span class="nb">Number</span><span class="p">(</span><span class="mi">2</span><span class="p">)).</span><span class="nx">typeOf</span><span class="p">();</span>  <span class="c1">// &quot;object&quot;</span>
</pre></div>
<p>包含数字、字符串和布尔类型原始类型变量的类似代码都可以这么理解（因为原始类型变量是不可修改的，即使调用会修改<tt class="docutils literal">this</tt>对象的用户自定义方法也没有问题），因为<tt class="docutils literal">Number</tt>、<tt class="docutils literal">String</tt>和<tt class="docutils literal">Boolean</tt>都可以当构造函数使用，这样就容易理解为什么给这些原始类型变量定义属性不会产生错误，但也不会成功。例子见<a class="reference external" href="201608/javascript-primitive-types-and-objects-zh/">这篇文章</a>。<tt class="docutils literal">null</tt>和<tt class="docutils literal">undefined</tt>因为没有对应的构造函数，会产生<tt class="docutils literal">TypeError</tt>。</p>
<p>但如果用户把<tt class="docutils literal">Number</tt>、<tt class="docutils literal">String</tt>或<tt class="docutils literal">Boolean</tt>当构造函数使用，不但没什么好处，还容易产生问题：</p>
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="p">(</span><span class="k">new</span> <span class="nb">Boolean</span><span class="p">(</span><span class="kc">false</span><span class="p">))</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&quot;You shouldn&#39;t have done this!&quot;</span><span class="p">);</span>
<span class="p">}</span>  <span class="c1">// You shouldn&#39;t have done this!</span>
</pre></div>
<p>ECMAScript 2015引入<tt class="docutils literal">Symbol</tt>类型时“根治”了这个问题：</p>
<div class="highlight"><pre><span></span><span class="k">try</span> <span class="p">{</span>
    <span class="k">new</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s2">&quot;x&quot;</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
<span class="p">}</span>  <span class="c1">// TypeError: Symbol is not a constructor</span>
</pre></div>
<p>但这不妨碍以下代码的执行：</p>
<div class="highlight"><pre><span></span><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s2">&quot;a&quot;</span><span class="p">);</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// 1</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span>  <span class="c1">// undefined</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>  <span class="c1">// &quot;Symbol(a)&quot;</span>
<span class="nx">Symbol</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">typeOf</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">typeof</span> <span class="k">this</span><span class="p">;</span>
<span class="p">};</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">typeOf</span><span class="p">();</span>  <span class="c1">// &quot;object&quot;</span>
<span class="k">typeof</span> <span class="nx">a</span><span class="p">;</span>  <span class="c1">// &quot;symbol&quot;</span>
</pre></div>
<p>大概因为虽然不能用<tt class="docutils literal">Symbol</tt>，但能用<tt class="docutils literal">Object</tt>：</p>
<div class="highlight"><pre><span></span><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s2">&quot;a&quot;</span><span class="p">);</span>
<span class="p">(</span><span class="k">new</span> <span class="nb">Object</span><span class="p">(</span><span class="nx">a</span><span class="p">)).</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// 1</span>
<span class="p">(</span><span class="k">new</span> <span class="nb">Object</span><span class="p">(</span><span class="nx">a</span><span class="p">)).</span><span class="nx">x</span><span class="p">;</span>  <span class="c1">// undefined</span>
<span class="p">(</span><span class="k">new</span> <span class="nb">Object</span><span class="p">(</span><span class="nx">a</span><span class="p">)).</span><span class="nx">toString</span><span class="p">();</span>  <span class="c1">// &quot;Symbol(a)&quot;</span>
<span class="nx">Symbol</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">typeOf</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">typeof</span> <span class="k">this</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">(</span><span class="k">new</span> <span class="nb">Object</span><span class="p">(</span><span class="nx">a</span><span class="p">)).</span><span class="nx">typeOf</span><span class="p">();</span>  <span class="c1">// &quot;object&quot;</span>
</pre></div>
<p>编辑记录：</p>
<ul>
<li><p class="first">2016-08-10，补充</p>
<p>无论是用<tt class="docutils literal">Number</tt>、<tt class="docutils literal">String</tt>或<tt class="docutils literal">Boolean</tt>还是<tt class="docutils literal">Symbol</tt>创建封装对象都是便于理解的等价做法，不等于一定是这么实现的。</p>
</li>
<li><p class="first">2016-08-14，补充</p>
<p>只用<tt class="docutils literal">Object</tt>来创建原始类型封装对象是个不错的主意，因为它是专业做这件事的，<tt class="docutils literal">Object(x)</tt>（等价于<tt class="docutils literal">new Object(x)</tt>）返回值和<tt class="docutils literal">x</tt>的关系如下：</p>
<ul class="simple">
<li><tt class="docutils literal">x</tt>为<tt class="docutils literal">null</tt>或<tt class="docutils literal">undefined</tt>：空对象</li>
<li><tt class="docutils literal">x</tt>为其它原始类型：对应的封装对象</li>
<li><tt class="docutils literal">x</tt>是对象：<tt class="docutils literal">x</tt></li>
</ul>
</li>
</ul>
<p>参考资料：</p>
<ul class="simple">
<li><a class="reference external" href="https://www.safaribooksonline.com/library/view/javascript-the-definitive/9781449393854/ch03s06.html">Wrapper Objects - JavaScript: The Definitive Guide, 6th Edition</a></li>
<li><a class="reference external" href="http://adripofjavascript.com/blog/drips/javascripts-primitive-wrapper-objects.html">JavaScript's Primitive Wrapper Objects</a></li>
<li><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean - MDN JavaScript Reference</a></li>
<li><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">Object - MDN JavaScript Reference</a></li>
</ul>
<p>（完）</p>

    </div>
<nav class="nav sidebar">
    <div class="siteinfo">
        <div class="sitename"><a href="../..">coinfaces</a></div>
        <div class="tagline">Another Side of Life</div>
    </div>

        <a href="../../categories/">categories</a>
        <a href="../..">&larr; Home</a>
</nav><footer class="footer">
    <span>
        &copy; jpli &middot;
        under <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA 4.0</a>
    </span>
    |
    <span>
        Powered by:
        <a href="http://blog.getpelican.com/">Pelican</a>
    </span>
</footer></body>
</html>